﻿module tester


    let TileSet = [| 'A'; 'C'; 'G'; 'T' |]
    let TileSize = TileSet.Length

    let ExpCounting n l k1 k2 =
        Array.map(fun i -> i + 1) [|1 .. k1 * k2|]
(*let effingadd f u = f + u;;

//let whatsmyeffingsign num =
//    if num > 0.0 then "positive"
//    elif num < 0.0 then "negative"
//    else "zero";;

let whatsmyeffingsign = function
    | num when num > 0.0 -> "positive"
    | num when num < 0.0 -> "negative"
    | _ -> "zero"
*)
    let getComplement (dnaBase:char) =
        match dnaBase with
            | 'A' -> 'T'
            | 'C' -> 'G'
            | 'T' -> 'A'
            | 'G' -> 'C'
            | _ -> 'F'

    let generateComplement (word:char[]) = 
        word |> Array.map(fun elem -> getComplement elem)

    // TODO: make these all functional... this is kind of sloppy in all honesty
    // TODO: make a better hamming distance function
    let hammingDistance (x:char[]) (y:char[]) =
        let mutable count = 0
        for i = 0 to x.GetLength(0)-1 do
            if x.[i] <> y.[i] then count <- count + 1
        count

    let testBasicHammingConstraint (words:char[][]) (k1:int) =
        // x = words[i-1]
        // y = words[i]
        let tester (x:char[]) (y:char[]) = 
            //let compX = generateComplement x
            hammingDistance x y
        for i = 1 to words.GetLength(0)-1 do
            let count = tester words.[i-1] words.[i]
            if count < k1 then
                failwith "Basic Hamming Constraint failed on index %d and %d" (i-1) i

    let testReverseComplementaryConstraint (words:char[][]) (k2:int) =
        let tester (x:char[]) (y:char[]) =
            let compRevX = Array.rev (generateComplement x)
            ExpCount.HammingDistance compRevX y
        for i = 1 to words.GetLength(0)-1 do
            let count = tester words.[i-1] words.[i]
            if count < k2 then
                failwith "Reverse Complementary Constraint failed on index %d and %d" (i-1) i

    let testSelfReverseComplementaryConstraint (words:char[][]) (k3:int) =
        let tester (y:char[]) =
            let compRevY = Array.rev (generateComplement y)
            ExpCount.HammingDistance compRevY y
        for i = 0 to words.GetLength(0)-1 do
            let count = tester words.[i]
            if count < k3 then
                failwith "Self Reverse Complementary Constraint failed on index %d" i

    let testShiftingHammingConstraint (words:char[][]) (k4:int) =
        let upperLimit = words.[0].GetLength(0)
        let lowerLimit = words.[0].GetLength(0)-k4
        let tester (x:char[]) (y:char[]) (i:int) =
            ExpCount.HammingDistance y.[0..(i-1)] x.[(upperLimit-i)..(upperLimit-1)]
        for j = 1 to words.GetLength(0)-1 do
            for i = lowerLimit to upperLimit do
                let count = tester words.[j-1] words.[j] i
                if count < (k4-(upperLimit-i)-1) then
                    failwith "Shifting Hammond Constraint failed on index %d and %d" (j-1) j

    let testShiftingReverseComplementaryConstraint (words:char[][]) (k5:int) =
        // globals
        let upperLimit = words.[0].GetLength(0)
        let lowerLimit = words.[0].GetLength(0)-k5

        // first test
        let testerA (x:char[]) (y:char[]) (i:int) =
            let compRevX = Array.rev (generateComplement x)
            ExpCount.HammingDistance y.[0..(i-1)] x.[0..(i-1)]
        for j = 1 to words.GetLength(0)-1 do
            for i = lowerLimit to upperLimit do
                let count = testerA words.[j-1] words.[j] i
                if count < (k5-(upperLimit-i)-1) then
                    failwith "Shifting Reverse Complementary Constraint failed test A on index %d and %d" (j-1) j

        // second test
        let testerB (x:char[]) (y:char[]) (i:int) =
            let compRevX = Array.rev (generateComplement x)
            ExpCount.HammingDistance y.[(y.GetLength(0)-i)..(y.GetLength(0)-1)] compRevX.[(compRevX.GetLength(0)-i)..(compRevX.GetLength(0)-1)]
        for j = 1 to words.GetLength(0)-1 do
            for i = lowerLimit to upperLimit do
                let count = testerB words.[j-1] words.[j] i
                if count < (k5-(upperLimit-i)-1) then
                    failwith "Shifting Reverse Complementary Constraint failed test B on index %d and %d" (j-1) j

    let testShiftingSelfReverseComplementaryConstraint (words:char[][]) (k6:int) =
        // globals
        let upperLimit = words.[0].GetLength(0)
        let lowerLimit = words.[0].GetLength(0)-k6

        // first test
        let testerA (y:char[]) (i:int) =
            let compRevY = Array.rev (generateComplement y)
            ExpCount.HammingDistance y.[0..i-1] compRevY.[0..i-1]
        for j = 0 to words.GetLength(0)-1 do
            for i = lowerLimit to upperLimit do
                let count = testerA words.[j] i
                if count < (k6-(upperLimit-i)) then
                    failwith "Shifting Self Reverse Complementary Constraint failed test A on index %d" j

        // second test
        let testerB (y:char[]) (i:int) =
            let compRevY = Array.rev (generateComplement y)
            ExpCount.HammingDistance y.[(upperLimit-i)..upperLimit-1] compRevY.[(upperLimit-i)..upperLimit-1]
        for j = 0 to words.GetLength(0)-1 do
            for i = lowerLimit to upperLimit do
                let count = testerB words.[j] i
                if count < (k6-(upperLimit-i)) then
                    failwith "Shifting Self Reverse Complementary Constraint failed test B on index %d" j

    let testGCContentConstraint (words:char[][]) (gamma:float) =
        if gamma > 1.0 then failwith "gamma must be a fractional float"
        let tester (y:char[]) =
            let sumG = Seq.sum ((Array.map(fun elem -> if (elem = 'G') then 1 else 0) y) :> seq<int>)
            let fracG = float(sumG)/float(y.Length)
            let sumC = Seq.sum ((Array.map(fun elem -> if (elem = 'C') then 1 else 0) y) :> seq<int>)
            let fracC = float(sumC)/float(y.Length)
            (fracG, fracC)
        for j = 0 to words.GetLength(0)-1 do
            let frac = tester words.[j]
            if ((fst frac) > gamma) || ((snd frac) > gamma) then
                failwith "GC Content Constraint failed test on index %d" j

    let testConsecutiveBaseConstraint (words:char[][]) (d:int) =
        if d < 2 then failwith "d must be 2 or greater"
        let tester (y:char[]) =
            let mutable lastChar = '?'
            let mutable count = 1
            // i don't really want to do loops... must be a better way
            for j = 0 to y.Length-1 do
                if y.[j] = lastChar then
                    count <- count + 1
                else
                    count <- 1
                    lastChar <- y.[j]
            count
        for i = 0 to words.GetLength(0)-1 do
            let count = tester words.[i]
            if count > d then
                failwith "Consecutive Base Constraint failed test on index %d" i